package finance

import (
	"github.com/gin-gonic/gin"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"likeadmin/admin/schemas/req"
	"likeadmin/admin/schemas/resp"
	"likeadmin/config"
	"likeadmin/constant"
	"likeadmin/core/request"
	"likeadmin/core/response"
	"likeadmin/dao"
	"likeadmin/model"
	"likeadmin/util"
	"strconv"
	"time"
)

type IRechargeOrderService interface {
	List(c *gin.Context, page request.PageReq, listReq req.RechargeOrderListReq) (res response.PageResp, e error)
	Refund(c *gin.Context, req req.RechargeOrderRefundReq) (e error)
}

// NewRechargeOrderService 初始化
func NewRechargeOrderService(db *gorm.DB, dao *dao.Query, refundSrv IRefundRecordService, refundLogSrv IRefundLogService) IRechargeOrderService {
	return &rechargeOrderService{db: db, dao: dao, refundSrv: refundSrv, refundLogSrv: refundLogSrv}
}

// rechargeOrderService 【请填写功能名称】服务实现类
type rechargeOrderService struct {
	db           *gorm.DB
	dao          *dao.Query
	refundSrv    IRefundRecordService
	refundLogSrv IRefundLogService
}

// List 【请填写功能名称】列表
func (srv rechargeOrderService) List(c *gin.Context, page request.PageReq, listReq req.RechargeOrderListReq) (res response.PageResp, e error) {
	// 分页信息
	limit := page.PageSize
	offset := page.PageSize * (page.PageNo - 1)
	// 总数
	var count int64
	m := srv.dao.RechargeOrder
	u := srv.dao.User
	q := m.WithContext(c)

	// 用户编号和手机号查询
	if listReq.UserInfo != "" {
		userInfo, _ := strconv.ParseInt(listReq.UserInfo, 10, 32)
		q = q.Where(q.Where(m.UserSn.Eq(int32(userInfo))).
			Or(m.Mobile.Eq(listReq.UserInfo)))
	}
	// 订单号查询
	if listReq.Sn != "" {
		q = q.Where(m.Sn.Eq(listReq.Sn))
	}

	// 支付方式
	if listReq.PayWay > 0 {
		q = q.Where(m.PayWay.Eq(listReq.PayWay))
	}
	// 支付状态
	if listReq.PayStatus > 0 {
		q = q.Where(m.PayStatus.Eq(listReq.PayStatus))
	}

	// 时间范围查询
	if listReq.StartTime != "" && listReq.EndTime != "" {
		startTime, err := util.DateToTimestamp(listReq.StartTime)
		if e = response.CheckErr(err, "DateToTimestamp err"); e != nil {
			return
		}

		endTime, err := util.DateToTimestamp(listReq.EndTime)
		if e = response.CheckErr(err, "DateToTimestamp err"); e != nil {
			return
		}

		q = q.Where(m.CreateTime.Gt(int32(startTime)), m.CreateTime.Lt(int32(endTime)))
	}

	count, err := q.Count()
	if e = response.CheckErr(err, "List Count err"); e != nil {
		return
	}

	var lists []resp.RechargeOrderResp
	objs, err := q.Preload(m.User.Select(u.ID, u.Avatar)).Limit(limit).Offset(offset).Find()
	if e = response.CheckErr(err, "List Find err"); e != nil {
		return
	}

	for _, obj := range objs {
		obj.User.Avatar = util.UrlUtil.ToAbsoluteUrl(obj.User.Avatar)
	}

	response.Copy(&lists, objs)

	return response.PageResp{
		PageNo:   page.PageNo,
		PageSize: page.PageSize,
		Count:    count,
		Lists:    lists,
	}, nil
}

func (srv rechargeOrderService) Refund(c *gin.Context, refundReq req.RechargeOrderRefundReq) (err error) {
	tx := srv.dao.Begin()
	m := srv.dao.RechargeOrder
	u := srv.dao.User
	q := m.WithContext(c)

	defer func() {
		if recover() != nil || err != nil {
			_ = tx.Rollback()
		}
	}()

	order, e := q.Where(m.ID.Eq(refundReq.RechargeId)).Preload(m.User.Select(u.ID, u.UserMoney, u.Sn)).Take()
	// 校验
	if err = response.CheckErrDBNotRecord(e, "数据不存在!"); err != nil {
		return
	}
	if err = response.CheckErr(e, "Refund First err"); err != nil {
		return
	}

	if order.PayStatus != constant.PayWayStatusPaid {
		tx.Rollback()
		return response.Failed.Make("订单未支付，不能退款")
	}

	if order.User.UserMoney < order.OrderAmount {
		tx.Rollback()
		return response.Failed.Make("用户余额小于订单金额，不能退款")
	}

	if order.RefundStatus == constant.RefundStatusRefunded {
		tx.Rollback()
		return response.Failed.Make("已退款订单不能重复退款")
	}

	// 给余额和充值总数减去金额
	_, e = tx.User.WithContext(c).Where(u.ID.Eq(order.User.ID)).UpdateSimple(u.UserMoney.Sub(order.OrderAmount))
	if err = response.CheckErr(e, "扣除用户余额失败"); err != nil {
		return
	}
	// 设置充值订单退款状态为已退款 防止重复退款
	_, e = tx.RechargeOrder.WithContext(c).Where(m.ID.Eq(order.ID)).UpdateSimple(m.RefundStatus.Value(constant.RefundStatusRefunded))
	if err = response.CheckErr(e, "设置充值订单退款状态为已退款时失败"); err != nil {
		return
	}

	UserMoney := decimal.NewFromFloat(order.User.UserMoney).Round(2)
	Num := decimal.NewFromFloat(order.OrderAmount).Round(2)
	leftAmount, _ := UserMoney.Sub(Num).Float64()

	// 写入余额变化日志
	e = tx.WithContext(c).UserAccountLog.Create(&model.UserAccountLog{
		Sn:           order.User.Sn,
		UserID:       order.User.ID,
		ChangeObject: constant.GetChangeObject(constant.UmDecRechargeRefund),
		ChangeType:   constant.UmDecRechargeRefund,
		Action:       constant.DEC,
		ChangeAmount: order.OrderAmount,
		LeftAmount:   leftAmount,
		SourceSn:     order.Sn, // 关联单号
		CreateTime:   int32(time.Now().Unix()),
	})
	if err = response.CheckErr(e, "UserAccountLog Create err"); err != nil {
		return
	}

	// 写入退款记录
	refundRecord := &model.RefundRecord{
		Sn:            srv.refundSrv.GenerateSN("RF", 4),
		UserID:        order.UserID,
		UserSn:        order.User.Sn,
		OrderID:       order.ID,
		OrderSn:       order.Sn,
		OrderType:     constant.OrderTypeRecharge,
		OrderAmount:   order.OrderAmount,
		RefundAmount:  order.OrderAmount,
		TransactionID: order.TransactionID,
		RefundWay:     constant.RefundWayOnline,
		RefundType:    constant.RefundTypeBack,
		RefundStatus:  constant.RefundStatusRefunding,
		CreateTime:    int32(time.Now().Unix()),
	}
	e = tx.WithContext(c).RefundRecord.Create(refundRecord)
	if err = response.CheckErr(e, "RefundRecord Create err"); err != nil {
		return
	}

	// 获取管理员Id
	adminId, _ := c.Get(config.AdminConfig.ReqAdminIdKey)

	// 并写入退款日志
	e = tx.WithContext(c).RefundLog.Create(&model.RefundLog{
		Sn:           srv.refundLogSrv.GenerateSN("RL", 4),
		RecordID:     refundRecord.ID,
		UserID:       order.UserID,
		HandleID:     int32(adminId.(uint)),
		OrderAmount:  refundRecord.OrderAmount,
		RefundAmount: refundRecord.RefundAmount,
		RefundStatus: constant.RefundStatusRefunding,
		CreateTime:   int32(time.Now().Unix()),
	})
	if err = response.CheckErr(e, "RefundLog Create err"); err != nil {
		return
	}

	// TODO 执行远程退款后 更改退款状态

	return tx.Commit()
}
